﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using SomethingSpacialClassLib.Web;
using System.Windows.Ria;
using System.Windows.Printing;
using System.IO;
using Elisy.Barcode;
using Microsoft.Maps.MapControl;
using System.Text;
using SomethingSpacial.RouteService;
using SomethingSpacial.GeocodingService;
using System.Text.RegularExpressions;

namespace SomethingSpacial.Views.MixItUp
{
    public partial class PrintBadgeMapPage : ChildWindow
    {


        private CaptureSource CapSrc = new CaptureSource();
        private VideoBrush MyVideoBrush = new VideoBrush();
        private bool VideoIsPlaying = false;

        //These are accessed later to calculate the route.
        internal GeocodeResult[] geocodeResults;

        public PrintBadgeMapPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(PrintBadgeMapPage_Loaded);
            AddressMapButton.Click += new RoutedEventHandler(AddressMapButton_Click);
        }

        void PrintBadgeMapPage_Loaded(object sender, RoutedEventArgs e)
        {

            StartCamBtn.Click += new RoutedEventHandler(StartCamBtn_Click);
            UploadPictureBtn.Click += new RoutedEventHandler(UploadPictureBtn_Click);
            btnPrintPreview.Click += new RoutedEventHandler(btnPrintPreview_Click);
            btnPrint.Click += new RoutedEventHandler(btnPrint_Click);
            btnPrintCancel.Click += new RoutedEventHandler(btnPrintCancel_Click);

            var ctx = new SomethingSpacialContext();
            EntityQuery<Event> latestEventQuery = ctx.GetEventLatestQuery();
            var loadOperation = ctx.Load(latestEventQuery);


            if (!loadOperation.HasError)
                loadOperation.Completed += (s, ev) =>
                {
                    Event evt = loadOperation.Entities.SingleOrDefault();

                    if (evt.EVT_Description.Length > 350)
                    {
                        evt.EVT_Description = evt.EVT_Description.Substring(0, 350) + "...";
                    }

                    LayoutRoot.DataContext = evt;


                    EntityQuery<SomethingSpacialClassLib.Web.Location> eventLocation = ctx.GetLocationsQuery();
                    var loadLocation = ctx.Load(eventLocation);

                    loadLocation.Completed += (locSender, args) =>
                    {
                        SomethingSpacialClassLib.Web.Location loc = loadLocation.Entities.Where(l => l.LOC_Id == evt.EVT_Location).SingleOrDefault();

                        textBoxToAddress.Text = formatAddress(loc);
                        textBoxFromAddress.Text = AppServices.WebContext.Current.User.Address;
                    };

                    textBlockAttendeeName.Text = AppServices.WebContext.Current.User.FriendlyName;
                    BarcodeEngine be = new BarcodeEngine();
                    textBlockAttendeeId.Text = be.Code39(AppServices.WebContext.Current.User.GetIdentity().ToString().ToUpper());

                };
        }

        void btnPrintCancel_Click(object sender, RoutedEventArgs e)
        {
            printGridCanvas.Visibility = Visibility.Collapsed;
        }

        string formatAddress(SomethingSpacialClassLib.Web.Location eventLocation)
        {

            String address = "";
            address += String.IsNullOrEmpty(eventLocation.LOC_StreetAddress1) ? "" : eventLocation.LOC_StreetAddress1 + ", ";
            address += String.IsNullOrEmpty(eventLocation.LOC_StreetAddress2) ? "" : eventLocation.LOC_StreetAddress2 + ", ";
            address += String.IsNullOrEmpty(eventLocation.LOC_City) ? "" : eventLocation.LOC_City + ", ";
            address += String.IsNullOrEmpty(eventLocation.LOC_Postalcode) ? "" : eventLocation.LOC_Postalcode + ", ";
            address += String.IsNullOrEmpty(eventLocation.LOC_State) ? "" : eventLocation.LOC_State + ", ";
            address += String.IsNullOrEmpty(eventLocation.LOC_Country) ? "" : eventLocation.LOC_Country + ", ";

            address = address.Length > 0 ? address.Substring(0, address.Length - 2) : address;

            return address;

        }
        void btnPrintPreview_Click(object sender, RoutedEventArgs e)
        {
            preparePrintData();

        }


        void btnPrint_Click(object sender, RoutedEventArgs e)
        {

            PrintDocument badge2Print = new PrintDocument();
            badge2Print.DocumentName = "EventAttendeeBadge";

             badge2Print.EndPrint += (owner, args) =>
             {
                 printGridCanvas.Visibility = Visibility.Collapsed;             
             };

            badge2Print.PrintPage += (owner, args) => { args.PageVisual = printGrid; };
            badge2Print.Print();
        }


        void preparePrintData()
        {

            textBlockAttendeeIdPrint.Text = textBlockAttendeeId.Text;
            textBlockAttendeeNamePrint.Text = textBlockAttendeeNamePrint.Text;
            textBlockMeetingTimePrint.Text = textBlockMeetingTime.Text;
            textBlockMeetingTitlePrint.Text = textBlockMeetingTitle.Text;

            imageRectanglePrint.Fill = WebcamRectangle.Fill;

            printGridCanvas.Visibility = Visibility.Visible;
        }



        void UploadPictureBtn_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog pfd = new OpenFileDialog();
            pfd.Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
            pfd.FilterIndex = 1;

            if ((bool)pfd.ShowDialog())
            {
                Stream stream = pfd.File.OpenRead();
                BitmapImage bi = new BitmapImage();
                bi.SetSource(stream);
                ImageBrush brush = new ImageBrush();
                brush.ImageSource = bi;
                WebcamRectangle.Fill = brush;
            }
        }

        void StartCamBtn_Click(object sender, RoutedEventArgs e)
        {
            if (!VideoIsPlaying)
            {
                VideoIsPlaying = true;
                if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
                {
                    MyVideoBrush.SetSource(CapSrc);
                    WebcamRectangle.Fill = MyVideoBrush;
                    CapSrc.Start();
                    StartCamBtn.Content = "Stop Web Cam";
                }
            }
            else
            {
                VideoIsPlaying = false;
                CapSrc.Stop();
                StartCamBtn.Content = "Start Web Cam";
            }

        }


        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }


        //--------------------------MAP RELATED CODE-----------------------------------
        void AddressMapButton_Click(object sender, RoutedEventArgs e)
        {
            clearBingMap();
            DirectionsTextBlock.Text = "";
            // Initialize the length of the results array. In this sample we have two waypoints.
            geocodeResults = new GeocodeResult[2];

            Geocode(textBoxFromAddress.Text, 0);
            Geocode(textBoxToAddress.Text, 1);
        }

        void clearBingMap() { 

            int count = BingMap.Children.Count;
            for(int i=0; i<count; i++){
                BingMap.Children.RemoveAt(0);
            }
        }

        // This method accepts a geocode query string as well as a ‘waypoint index’, which will be used to track each asynchronous geocode request.
        private void Geocode(string address, int waypointIndex)
        {
            // Create the service variable and set the callback method using the GeocodeCompleted property.
            GeocodeServiceClient GeocodingServiceClient = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
            GeocodingServiceClient.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(GeocodingServiceClient_GeocodeCompleted);

            // Set the credentials and the geocode query, which could be an address or location.
            GeocodeRequest request = new GeocodeRequest();

            /*without namespace - conflict with RouteService.Credentials can occur*/
            request.Credentials = new GeocodingService.Credentials();
            request.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)BingMap.CredentialsProvider).ApplicationId;
            request.Query = address;

            // Make the asynchronous Geocode request, using the ‘waypoint index’ as 
            //   the user state to track this request and allow it to be identified when the response is returned.
            GeocodingServiceClient.GeocodeAsync(request, waypointIndex);

        }

        void GeocodingServiceClient_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
        {
            /*sample app directly assigned Result loc. to Location; but this raises an error:
             * "cannot assign GeocodeLocation to Location*/
            if (e.Result.Results.Count == 0)
            {
                return;
            }

            /*without namespace - conflict with RouteService.GeocodeLocation can occur*/
            GeocodingService.GeocodeLocation geocodeCenter = e.Result.Results[0].Locations[0];
            Microsoft.Maps.MapControl.Location center = new Microsoft.Maps.MapControl.Location(geocodeCenter.Latitude, geocodeCenter.Longitude);
            BingMap.Center = center;
            Pushpin pushpin = new Pushpin();
            pushpin.Location = center;
            BingMap.Children.Add(pushpin);


            /*adding*/
            // Retrieve the user state of this response (the ‘waypoint index’) to identify which geocode request 
            // it corresponds to.
            int waypointIndex = System.Convert.ToInt32(e.UserState);

            // Retrieve the GeocodeResult for this response and store it in the global variable geocodeResults, using
            //   the waypoint index to position it in the array.
            geocodeResults[waypointIndex] = e.Result.Results[0];

            // Look at each element in the global gecodeResults array to figure out if more geocode responses still 
            //   need to be returned.
            bool doneGeocoding = true;

            foreach (GeocodeResult gr in geocodeResults)
            {
                if (gr == null)
                {
                    doneGeocoding = false;
                }
            }

            // If the geocodeResults array is totally filled, then calculate the route.
            if (doneGeocoding)
                CalculateRoute(geocodeResults);

        }


        // This method makes the initial CalculateRoute asynchronous request using the results of the Geocode Service.
        private void CalculateRoute(GeocodeResult[] results)
        {
            // Create the service variable and set the callback method using the CalculateRouteCompleted property.
            RouteServiceClient routeService = new RouteServiceClient("BasicHttpBinding_IRouteService");
            routeService.CalculateRouteCompleted += new EventHandler<CalculateRouteCompletedEventArgs>(routeService_CalculateRouteCompleted);

            // Set the token.
            RouteRequest routeRequest = new RouteRequest();
            routeRequest.Credentials = new RouteService.Credentials();
            routeRequest.Credentials.ApplicationId = ((ApplicationIdCredentialsProvider)BingMap.CredentialsProvider).ApplicationId;

            // Return the route points so the route can be drawn.
            routeRequest.Options = new RouteOptions();
            routeRequest.Options.RoutePathType = RoutePathType.Points;

            // Set the waypoints of the route to be calculated using the Geocode Service results stored in the geocodeResults variable.
            routeRequest.Waypoints = new System.Collections.ObjectModel.ObservableCollection<Waypoint>();
            foreach (GeocodeResult result in results)
            {
                routeRequest.Waypoints.Add(GeocodeResultToWaypoint(result));
            }

            // Make the CalculateRoute asnychronous request.
            routeService.CalculateRouteAsync(routeRequest);
        }

        //Helper method
        private RouteService.Waypoint GeocodeResultToWaypoint(GeocodeResult result)
        {
            Waypoint waypoint = new Waypoint();
            waypoint.Description = result.DisplayName;
            waypoint.Location = new RouteService.Location();
            waypoint.Location.Latitude = result.Locations[0].Latitude;
            waypoint.Location.Longitude = result.Locations[0].Longitude;
            return waypoint;
        }

        // This is the callback method for the CalculateRoute request.
        private void routeService_CalculateRouteCompleted(object sender, CalculateRouteCompletedEventArgs e)
        {

            // If the route calculate was a success and contains a route, then draw the route on the map.
            if ((e.Result.ResponseSummary.StatusCode == RouteService.ResponseStatusCode.Success) & (e.Result.Result.Legs.Count != 0))
            {
                // Set properties of the route line you want to draw.
                Color routeColor = Colors.Blue;
                SolidColorBrush routeBrush = new SolidColorBrush(routeColor);
                MapPolyline routeLine = new MapPolyline();
                routeLine.Locations = new LocationCollection();
                routeLine.Stroke = routeBrush;
                routeLine.Opacity = 0.65;
                routeLine.StrokeThickness = 5.0;

                // Retrieve the route points that define the shape of the route.
                foreach (RouteService.Location p in e.Result.Result.RoutePath.Points)
                {
                    routeLine.Locations.Add(new Microsoft.Maps.MapControl.Location(p.Latitude, p.Longitude));
                }

                // Add a map layer in which to draw the route.
                MapLayer myRouteLayer = new MapLayer();
                BingMap.Children.Add(myRouteLayer);

                // Add the route line to the new layer.
                myRouteLayer.Children.Add(routeLine);

                // Figure the rectangle which encompasses the route. This is used later to set the map view.
                LocationRect rect = new LocationRect(routeLine.Locations[0], routeLine.Locations[routeLine.Locations.Count - 1]);

                // For each geocode result (which are the waypoints of the route), draw a dot on the map.
                foreach (GeocodeResult gr in geocodeResults)
                {
                    Ellipse point = new Ellipse();
                    point.Width = 10;
                    point.Height = 10;
                    point.Fill = new SolidColorBrush(Colors.Red);
                    point.Opacity = 0.65;
                    Microsoft.Maps.MapControl.Location location = new Microsoft.Maps.MapControl.Location(gr.Locations[0].Latitude, gr.Locations[0].Longitude);
                    MapLayer.SetPosition(point, location);
                    MapLayer.SetPositionOrigin(point, PositionOrigin.Center);

                    // Add the drawn point to the route layer.                    
                    myRouteLayer.Children.Add(point);
                }

                // Set the map view using the rectangle which bounds the rendered route.
                BingMap.SetView(rect);

    
                // Update the UI with the directions

                // The result is an RouteResponse Object
                RouteResponse routeResponse = e.Result;

                // Iterate through each itinerary item to get the route directions
                RouteLeg leg;
                StringBuilder directions = new StringBuilder();

                if (routeResponse.Result.Legs.Count > 0)
                {
                    for (int k = 0; k < routeResponse.Result.Legs.Count; k++)
                    {
                        leg = routeResponse.Result.Legs[k];

                        if (k > 0) /*only occurs if route has multiple legs*/
                        {
                            directions.Append(string.Format("{0}.Section of Route", k + 1));
                            directions.AppendLine();
                        }
                        for (int i = 0; i < leg.Itinerary.Count; i++)
                        {

                            directions.Append(string.Format("{0}. {1}", i+1, leg.Itinerary[i].Text));
                            directions.AppendLine();
                        }
                    }

                    //Remove all Bing Maps tags around keywords.  
                    Regex regex = new Regex("<[/a-zA-Z:]*>",
                    RegexOptions.IgnoreCase | RegexOptions.Multiline);
                    DirectionsTextBlock.Text = regex.Replace(directions.ToString(), string.Empty);
                }
                else
                    DirectionsTextBlock.Text = "No Route found";

            }

        }


    }
}

